package com.lagou.filter;

import com.google.gson.internal.bind.util.ISO8601Utils;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {
    private ConcurrentHashMap<String, TreeMap<Long, Long>> TPMap = new ConcurrentHashMap<>();
//    private TreeMap<Long, Long> TPMap = new TreeMap<>();
    private static final ScheduledExecutorService TP_MONITOR = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("TPMonitor", true));

    public TPMonitorFilter() {
        TP_MONITOR.scheduleWithFixedDelay(this::CalculateTP, 5000L, 5000L, TimeUnit.MILLISECONDS);
    }


    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        String methodName = invocation.getMethodName();
        long executeTime;
        try {
            // 执行方法
            return invoker.invoke(invocation);
        } finally {
            executeTime = System.currentTimeMillis() - startTime;
            putTimeToMap(methodName, startTime, executeTime);
        }
    }

    public synchronized void putTimeToMap(String name, Long startTime, Long executeTime) {
        if (!TPMap.containsKey(name)) {
            TPMap.put(name, new TreeMap<>());
        }
        TPMap.get(name).put(startTime, executeTime);
    }

    private synchronized HashMap<String, List<Long>> cutOneMin() {
        Long oneMinAgo = System.currentTimeMillis() - 60000;
        TPMap.replaceAll((k, v) -> new TreeMap<>(v.tailMap(oneMinAgo)));
        HashMap<String, List<Long>> result = new HashMap<>();
        for (Map.Entry<String, TreeMap<Long, Long>> e:TPMap.entrySet()) {
            List<Long> collect = e.getValue().values().stream().sorted().collect(Collectors.toList());
            if (collect.size() == 0) {
                TPMap.remove(e.getKey());
            } else {
                result.put(e.getKey(), collect);
            }
        }
        return result;
    }

    public void CalculateTP() {
        HashMap<String, List<Long>> executeTimeList = cutOneMin();
        printTP(executeTimeList, 90);
        printTP(executeTimeList, 99);
    }

    public void printTP(HashMap<String, List<Long>> targetList, int num) {
        if (targetList.size() == 0) {
            System.out.println("no invoker occur, the TP"+num+": null");
        } else {
            for (Map.Entry<String, List<Long>> e :targetList.entrySet()) {
                int n = (e.getValue().size()-1) * num / 100;
                System.out.println("the TP" + num + " in "+ e.getKey() +": " + e.getValue().get(n) + "ms");
            }
        }
    }
}
